'''
5/9完成，第一个可以实现CNN网络辅助预测音符的程序
'''


import numpy as np
from scipy.signal import butter, lfilter, sosfilt, cheby1, freqz
from scipy.io import wavfile
from scipy.signal import find_peaks
import matplotlib.pyplot as plt
from lib.freq_gen import freq_lis, freq_lis_small
from lib.data_output import write_to_py_file
import time
from datetime import datetime
from keras.models import load_model


file_name = "test_2.wav"
ctrl_contrast = 1
ctrl_hide = 0.0

def normalize(y, ctrl_contrast=1.0, ctrl_hide=0.0, range_ctrl = 0, max_val = None):

    """
    归一化数组, 将给定数组化为每一项与数组最大值的比.

    参数:
        y (numpy.ndarray): 要归一化的音频数据.
        ctrl_contrast (float): 对比度, 即每个数据归一化后进行一次幂运算, 该参数作为指数.小
            于1则减小对比度, 大于1则提高对比度.
        ctrl_hide (float): 隐藏值, 小于该值的项不予显示, 或设置为小于, 以提高所有项可见性.
        range_ctrl (bool): 是否启用范围控制, 即将小于0和大于1的项化为0或1.
        max_val (float): 直接给定的最大值, 此种情况下, 忽略y自己的最大值.

    返回:
        y_n (numpy.ndarray): 归一化后的数组y.
    """

    if max_val == None:
        y_max = np.max(np.abs(y))
    else:
        y_max = max_val

    if y_max > 0:
        y_n = y / y_max
    else:
        y_n = y

    y_n = y_n ** ctrl_contrast

    y_n = y_n - ctrl_hide

    if range_ctrl == 1:
        for i in range(len(y_n)):
            if y_n[i] < 0:
                y_n[i] = 0
            if y_n[i] > 1:
                y_n[i] = 1

    return y_n

def analyze_wav(filename, do_fft = 1):

    """
    读取并分析WAV文件.

    参数:
        filename (string): 音频文件地址, 相对路径或绝对路径均可.
        do_fft (bool): 是否进行快速傅里叶变换， 若为1, 则读取完后自动进行一次FFT.

    返回:
        data (numpy.ndarray): 归一化后的数组y.
        sample_rate (int): WAV文件的采样率, 单位Hz.
        freq (numpy.ndarray): 启用FFT后生效, 给出有效的频率值.
        fft_magnitude (numpy.ndarray): 启用FFT后生效, 给出各频率值对应的幅值.
    """

    # 读取WAV文件
    sample_rate, data = wavfile.read(filename)

    # 转换立体声为单声道
    if len(data.shape) > 1:
        data = data.mean(axis=1)

    data = normalize(data).astype(np.float32)

    if do_fft == 1:
        # 计算FFT
        n = len(data)
        fft_result = np.fft.fft(data)
        fft_magnitude = np.abs(fft_result) / n  # 幅度归一化
        fft_magnitude = fft_magnitude[:n//2]    # 取单边频谱

        # 创建频率轴
        freq = np.fft.fftfreq(n, 1/sample_rate)[:n//2]
    else:
        freq = 0
        fft_magnitude = 0

    return data, sample_rate, freq, fft_magnitude

def analyze_data_by_time(data, sample_rate, average_power, time_interval=1):
    """
    对音频数据进行分时分析，提取每个时间段内超过平均功率一定阈值的频率分量。

    参数:
        data (numpy.ndarray): 音频数据（单声道，归一化后的浮点数数组）。
        sample_rate (int): 音频的采样率（每秒样本数）。
        average_power (float): 音频信号的平均功率。
        time_interval (int): STFT分析的时间间隔, 单位为秒。

    返回值:
        list: 一个列表，其中每个元素都是一个元组 (time_range, peak_freq)。
              time_range (tuple): 频率分量所在STFT区间的时间范围，格式为 (start_time, end_time)，单位为秒。
              peak_freq (float): 在该时间范围内，超过平均功率阈值的频率分量值，单位为Hz。
    """


    file_length = len(data)  # 音频数据的总长度（样本数）
    segment_size = int(sample_rate * time_interval)  # 每个时间段的样本数
    results = []  # 用于存储结果的列表

    # 循环遍历音频数据，按时间段进行处理
    for i in range(0, file_length, segment_size):
        segment = data[i:i + segment_size]  # 提取当前时间段的音频数据

        # 如果当前时间段的长度小于 segment_size，则用零填充，保证每个段长度一致，便于FFT计算
        if len(segment) < segment_size:
            segment = np.pad(segment, (0, segment_size - len(segment)), 'constant')

        n = len(segment)  # 当前时间段的样本数
        fft_result = np.fft.fft(segment)  # 对当前时间段的数据进行FFT计算
        fft_magnitude = np.abs(fft_result) / n  # 计算FFT结果的幅度谱，并进行归一化
        fft_magnitude = fft_magnitude[:n//2]  # 取单边频谱，因为FFT结果是对称的

        freq = np.fft.fftfreq(n, 1/sample_rate)[:n//2]  # 创建频率轴，对应于单边频谱的频率值

        valid_range = (freq >= 20) & (freq <= 20000)  # 设置有效频率范围为20Hz到20000Hz
        freq = freq[valid_range]  # 提取有效频率范围内的频率值
        fft_magnitude = fft_magnitude[valid_range]  # 提取有效频率范围内的幅度值

        # 查找幅度谱中的峰值
        # height: 峰值的最小高度，设置为平均功率的1.05倍，即峰值需要超过平均功率5%
        # distance: 最小峰间距，设置为频率分辨率的2^(1/24)倍，这个值需要根据具体音频内容进行调整
        peak_indices, _ = find_peaks(fft_magnitude, height = average_power * 0.05, prominence=0.005)
        peak_freqs = freq[peak_indices]  # 获取峰值对应的频率值

        # 计算当前时间段的时间范围
        start_time = i / sample_rate  # 当前时间段的起始时间，单位为秒
        end_time = min((i + segment_size) / sample_rate, file_length / sample_rate)  # 当前时间段的结束时间，单位为秒
        time_range = [start_time, end_time]  # 将起始时间和结束时间组成一个元组

        # 将结果添加到列表中
        for peak_freq in peak_freqs:
            results.append([time_range, peak_freq])  # 将时间范围和峰值频率组成一个元组，添加到结果列表中

    return results  # 返回结果列表

def bandpass_filter(data, lowcut, highcut, sample_rate, order=4, filter_type='cheby1', rp=1, use_sos=True):
    """
    设计并应用带通滤波器。

    参数:
        data (numpy.ndarray): 要滤波的音频数据。
        lowcut (float): 低截止频率 (Hz)。
        highcut (float): 高截止频率 (Hz)。
        sample_rate (int): 音频采样率 (Hz)。
        order (int): 滤波器阶数。
        filter_type (str): 滤波器类型，可以选择 'butterworth' 或 'cheby1'。
        rp (float): 通带最大允许的纹波，单位为 dB。只在 filter_type 为 'cheby1' 时使用。
        use_sos (bool): 是否使用二阶sections (second-order sections)

    返回:
        numpy.ndarray: 滤波后的音频数据。
    """
    nyquist = 0.5 * sample_rate
    low = lowcut / nyquist
    high = highcut / nyquist

    if low >= 1 or high >= 1:
        raise ValueError("截止频率高于奈奎斯特频率")

    if filter_type == 'butterworth':
        if use_sos:
            sos = butter(order, [low, high], btype='band', output='sos')
            filtered_data = sosfilt(sos, data)
        else:
            b, a = butter(order, [low, high], btype='band', output='ba')
            filtered_data = lfilter(b, a, data)
    elif filter_type == 'cheby1':
        if use_sos:
            sos = cheby1(order, rp, [low, high], btype='band', output='sos')
            filtered_data = sosfilt(sos, data)
        else:
            b, a = cheby1(order, rp, [low, high], btype='band', output='ba')
            filtered_data = lfilter(b, a, data)
    else:
        raise ValueError("不支持的滤波器类型")

    return filtered_data

def time_domain_intensity(data, window_size=None, win_time=None, sample_rate=44100):

    """
    计算时域上的强度分布，可以选择计算平均功率.

    参数:
        data (numpy.ndarray): 音频数据。
        window_size (float, 可选): 用于计算强度的滑动窗口大小(相对于信号长度的比例).
            如果为 None,则计算整个信号的平均功率。
        min_time (int, 可选): 用于直接规定窗口大小, 该项给出时, 忽略window_size, 单位为ms.
            若该项给出, 则需同时给定sample_rate用于计算时间长度.
        sample_rate(int, 可选): 给出信号的采样率, 单位为Hz, 用于计算时间.

    返回:
        average_power (float): 全时间范围内的平均强度.
        intensity(numpy.ndarray): 时域强度分布数据.
        输出为一个二维数组:
        [强度, 时间戳(单位ms)]
        为减小开销, 每个窗口内只有一条记录.

    """

    length = len(data)
    if window_size == None and win_time == None:
        # 计算整个信号的平均功率
        total_energy = np.sum(np.abs(data)**2)
        average_power = float(total_energy / length)  # 除以样本数
        return average_power  # 返回平均功率
    else:
        intensity = []

        if win_time != None:
            # 使用滑动窗口(给定时长)计算强度
            window_samples = int(win_time * sample_rate / 2000)  # 窗口大小的一半  \
            window_size = int(win_time * sample_rate / 1000)
            for i in range(0, length, window_size):
                start = max(0, i - window_samples)
                end = min(length, i + window_samples + 1)
                intensity.append([np.sum(np.abs(data[start:end])**2) / (window_size), i/sample_rate])  # 计算窗口内的平均功率
            return np.array(intensity)
        else:
            # 使用滑动窗口(比例)计算强度
            window_samples = int(window_size * length / 2)  # 窗口大小的一半
            window_size = int(window_size * length)
            for i in range(0, length, window_size):
                start = max(0, i - window_samples)
                end = min(length, i + window_samples + 1)
                intensity.append([np.sum(np.abs(data[start:end])**2) / (window_size), i/sample_rate])  # 计算窗口内的平均功率
            return np.array(intensity)

def draw_fig(data, sample_rate, freq, fft_magnitude):

    n = len(data)

    # 创建时域和频域图形
    plt.figure(figsize=(12, 8))

    # 时域波形
    plt.subplot(2, 1, 1)
    time = np.arange(n) / sample_rate
    plt.plot(time, data)
    plt.title('Time Domain Waveform')
    plt.xlabel('Time (s)')
    plt.ylabel('Amplitude')
    plt.xlim(0, time[-1])

    # 频域频谱（线性坐标）
    plt.subplot(2, 1, 2)
    plt.semilogx(freq, fft_magnitude)  # 直接使用线性幅度
    plt.title('Frequency Spectrum (Linear Scale)')
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Amplitude')
    plt.xlim(20, sample_rate/2)
    plt.ylim(0, 0.1)  # 根据实际情况调整y轴范围
    plt.grid(True)

    plt.tight_layout()
    plt.show()

def draw_data(data, title="Data Plot", xlabel="Index", ylabel="Value"):
    """
    绘制一个数据的图。

    参数:
        data (numpy.ndarray): 要绘制的数据。
        title (str, 可选): 图的标题。默认为 "Data Plot"。
        xlabel (str, 可选): 横坐标标签。默认为 "Index"。
        ylabel (str, 可选): 纵坐标标签。默认为 "Value"。
    """
    plt.figure(figsize=(12, 6))  # 创建一个图形
    plt.plot(data)  # 绘制数据
    plt.title(title)  # 设置标题
    plt.xlabel(xlabel)  # 设置横坐标标签
    plt.ylabel(ylabel)  # 设置纵坐标标签
    plt.grid(True)  # 添加网格
    plt.tight_layout()  # 自动调整子图参数, 使之填充整个图像区域
    plt.show()  # 显示图形

def draw_line(data, y = None):
    # 绘制散点图
    t = np.arange(len(data))
    data_n, trans = normalize(data, ctrl_contrast, ctrl_hide)

    if y == None:
        plt.figure(figsize=(12, 6))
        plt.scatter(t, data_n,
                    s=16, alpha=trans,  # 透明度由归一化强度决定
                    c=data_n, cmap='Reds',
                    marker = 's')  # 颜色由归一化强度决定
        plt.xlabel('data')
        plt.ylabel('n')
        plt.xlim(0, int(max(t)*1.1))
        plt.ylim(0, max(data_n)+1)  # 设定时间轴的范围
        plt.tight_layout()
        plt.show()
    else:
        y_lim = y
        y = np.zeros_like(data_n) + y_lim
        plt.figure(figsize=(12, 6))
        plt.scatter(t, y,
                    s=16, alpha=trans,  # 透明度由归一化强度决定
                    c=data_n, cmap='Reds',
                    marker = 's')  # 颜色由归一化强度决定
        plt.xlabel('y')
        plt.ylabel('x')
        plt.xlim(0, int(max(t)*1.1))
        plt.ylim(0, 2*y_lim)  # 设定时间轴的范围
        plt.tight_layout()
        plt.show()

def draw_time_freq_map(time, freq, intensity, x_lim = [], y_lim = [], ctrl_contrast=1.0, ctrl_hide=0.0):

    # 绘制散点图
    data_n = normalize(intensity, ctrl_contrast, ctrl_hide)

    plt.figure(figsize=(12, 6))

    plt.scatter(time, freq,
                s=16, alpha=data_n,  # 透明度由归一化强度决定
                c=data_n, cmap='Reds',
                marker = 's')  # 颜色由归一化强度决定

    plt.yscale('log')
    plt.xlabel('data')
    plt.ylabel('n')
    if x_lim == []:
        plt.xlim(int(min(time)*0.9), int(time*1.1))
    else:
        plt.xlim(x_lim[0], x_lim[1])
    if y_lim == []:
        plt.ylim(int(min(freq)*0.9), int(freq*1.1))
    else:
        plt.ylim(y_lim[0], y_lim[1])
    plt.tight_layout()
    plt.show()

def cwt_analyze(data, sample_rate, small=0, intensity_threshold=0.05, time_window=[], freq_to_analyze=[], time_del=1, time_offset=0):
    """
    使用带通滤波器和时域强度分析计算连续小波变换 (CWT) 数据，并在记录时进行归一化和过滤。

    参数:
        data (numpy.ndarray): 音频数据。
        sample_rate (int): 音频采样率 (Hz)。
        small (int):  选择使用哪个频率列表的标志 (0: freq_lis, 否则: freq_lis_small)。
        intensity_threshold (float):  强度阈值，低于此值的数据点不记录。
        time_window (list, optional): 时间窗口列表，包含两个元素 [start_time, end_time]，单位为秒。
                                      如果为空列表，则分析整个音频。默认为 []。
        freq_to_analyze (list, optional):  包含若干频率的列表。如果指定，则仅分析这些频率附近的频带。默认为 []。
        time_del(int): 时间分辨率, 默认1ms。

    返回:
        list: CWT 数据列表，每个元素都是 [time, frequency, normalized_intensity] 形式的列表。
    """
    cwt_data = []

    if freq_to_analyze == []:
        if small == 0:
            freq_list = freq_lis
        else:
            freq_list = freq_lis_small
    else:
        freq_list = []
        for freq in freq_to_analyze:
            for freq_info in freq_lis:
                if freq_info[1][0] < freq <= freq_info[1][1]:
                    freq_list.append(freq_info)
                    break  # 找到一个匹配的区间后就跳出内层循环

    average_power = time_domain_intensity(data)

    # 根据 time_window 截取数据
    start_index = 0
    end_index = len(data)

    if time_window:
        start_time = time_window[0]
        end_time = time_window[1]

        start_index = int(start_time * sample_rate)
        end_index = int(end_time * sample_rate)

        # 确保索引在有效范围内
        start_index = max(0, start_index)
        end_index = min(len(data), end_index)
        time_offset = time_offset + start_time  # 记录时间偏移量

    for freq_info in freq_list:
        center_freq = freq_info[0]
        lowcut, highcut = freq_info[1]

        #对计算时间范围内的数据进行滤波
        filtered_data = bandpass_filter(data[start_index:end_index], lowcut, highcut, sample_rate)

        #计算滤波后的数据的强度分布
        intensity = time_domain_intensity(filtered_data, win_time=time_del)

        intensity_max = np.max(intensity[:,0])

        for item in intensity:
            if item[0] >= intensity_max * intensity_threshold and item[0] >= average_power * (intensity_threshold * 5):
                cwt_data.append([item[1]+time_offset, center_freq, item[0]])

        current_time = datetime.now().strftime("%H:%M:%S")
        print("one freq finish", current_time)
        print(len(cwt_data))

    return cwt_data

def data_print(cwt_res, do_print=0):

    """
    根据连续小波变换（CWT）结果生成频率分量数据，并将时间戳放在每条数据的末尾。

    参数:
        cwt_res (numpy.ndarray): 经过 CWT 分析的结果数组，包含时间、频率和强度信息。
                                 结构为二维数组，每行格式为 [time, frequency, intensity]。

    返回值:
        numpy.ndarray: 一个二维数组，其中每行包含88个频率分量和一个时间戳。
                       如果某个时间戳缺少相应的频率数据，则补零。
    """

    # 获取所有的唯一时间戳
    t_lis = np.unique(cwt_res[:, 0])
    res_lis = []

    # 遍历每个唯一的时间戳
    for t in t_lis:
        # 获取该时间戳下的所有频率分量
        freqs_exist = cwt_res[cwt_res[:, 0] == t][:, 1]

        # 创建一个包含88个频率分量的数组，初始值为0
        data_row = np.zeros(89)

        for freq in freqs_exist:
            for n in range(len(freq_lis)):
                if freq>=freq_lis[n][1][0] and freq<=freq_lis[n][1][1]:
                    data_row[n] = cwt_res[(cwt_res[:, 0] == t) & (cwt_res[:, 1] == freq)][:, 2][0]

        # 对于每个时间戳的频率分量，进行归一化处理
        intensities = data_row[:-1]  # 排除时间戳
        transformed_intensities = np.abs(intensities[np.nonzero(intensities)])  # 取绝对值
        if transformed_intensities.size > 0:  # 确保至少有一个有效强度值
            max_val = np.max(transformed_intensities)
            normalized_intensities = transformed_intensities / max_val  # 归一化到0-1
            data_row[:-1][np.nonzero(intensities)] = normalized_intensities  # 更新原来的强度值

        # 在 data_row 的末尾插入时间戳
        data_row[-1] = t  # 在末尾插入时间戳
        res_lis.append(data_row)

    # 转换为二维数组
    result_array = np.array(res_lis)

    if do_print:
        # 打印结果
        for row in result_array:
            print(row)

    return result_array

def split_audio_into_segments(data, sample_rate, segment_length=20):
    """
    将音频数据按时间切分为固定长度的分段
    参数:
        data: 音频数据 (numpy数组)
        sample_rate: 采样率 (单位: Hz)
        segment_length: 每段时长 (单位: 秒, 默认20秒)
    返回:
        segments: 列表，每个元素为 (分段数据, 时间窗口)
                  时间窗口格式为 (start_time, end_time)
    """
    total_samples = len(data)
    samples_per_segment = segment_length * sample_rate
    segments = []

    start_sample = 0
    while start_sample < total_samples:
        end_sample = start_sample + samples_per_segment
        if end_sample >= total_samples:
            end_sample = total_samples
        segment_data = data[start_sample:end_sample]
        start_time = start_sample / sample_rate
        end_time = end_sample / sample_rate
        segments.append( (segment_data, [start_time, end_time]) )
        start_sample = end_sample  # 移动到下一个分段开始

    return segments

def process_segment(segment, sample_rate):
    """
    处理单个音频分段，执行频率分量检测和连续小波变换分析。

    参数:
        segment_data (numpy.ndarray): 当前分段的音频数据，形状为 (n_samples, ) 的浮点数组。
        time_window (list): 该分段对应原始音频的时间范围，格式为 [start_time, end_time]，
                            单位为秒。用于结果时间戳的全局对齐。
        sample_rate (int): 音频采样率，单位Hz，必须与原始音频一致。

    返回:
        numpy.ndarray: CWT分析结果数组，形状为 (n_detections, 3)，每行包含三个值:
                        [time, frequency, intensity]
                        - time (float): 检测时间戳（单位：秒），已对齐到原始音频的时间轴
                        - frequency (float): 检测到的频率值（单位：Hz）
                        - intensity (float): 归一化前的原始强度值，范围[0,1]
    """

    segment_data = segment[0]
    time_window = segment[1]
    # 计算本分段的平均功率
    average_power = time_domain_intensity(segment_data)

    # 分析时段内显著频率分量
    freq_peaks = analyze_data_by_time(segment_data, sample_rate, average_power)

    # 进行CWT分析（增加时间窗口参数）
    cwt_res_segment = []
    for peak_info in freq_peaks:
        # peak_info格式: ([时间窗口], 峰值频率)
        cwt_res_part = cwt_analyze(
            data=segment_data,
            sample_rate=sample_rate,
            small=0,
            intensity_threshold=0,
            time_window=peak_info[0],
            freq_to_analyze=[peak_info[1]],
            time_del=25,  # 更精细的时间分辨率
            time_offset= time_window[0]
        )
        cwt_res_segment.extend(cwt_res_part)

    return np.array(cwt_res_segment)

if __name__ == "__main__":

    # 记录开始时间
    start_time = time.time()

    cwt_res = []

    res_data, rate, res_freq, res_amp = analyze_wav(file_name, do_fft=0)
    print("finish load")

    segments_lis = split_audio_into_segments(res_data, rate, segment_length=20)

    print(len(segments_lis))
    for segment in segments_lis:
        print(segment[1])

    for segment in segments_lis:
        cwt_res.append(process_segment(segment, rate))

    cwt_res = np.vstack([item for item in cwt_res if len(item) > 0])

    cwt_res[:,2] = normalize(cwt_res[:,2], range_ctrl=1)

    print(len(cwt_res))

    note_pre_model = load_model('note_pre.keras')
    strict_note_pre_model = load_model('note_pre_0.keras')
    loose_note_pre_model = load_model('note_pre.keras')

    data_to_pre = data_print(cwt_res)

    #pre_data = note_pre_model.predict(np.array(list(item[0:88] for item in data_to_pre)))
    strict_pre_data = strict_note_pre_model.predict(np.array(list(item[0:88] for item in data_to_pre)))
    loose_pre_data = loose_note_pre_model.predict(np.array(list(item[0:88] for item in data_to_pre)))

    pre_data = (strict_pre_data + loose_pre_data)/2

    data_to_draw_fig = []
    for index, item in enumerate(pre_data):
        print(len(item))
        for n in range(88):
            if pre_data[index][n] >= 0.8:
                data_to_draw_fig.append([data_to_pre[index][-1], freq_lis[n][0], 1])
            elif pre_data[index][n] < 0.8 and pre_data[index][n] >= 0.5:
                data_to_draw_fig.append([data_to_pre[index][-1], freq_lis[n][0], pre_data[index][n]**2])
            else:
                data_to_draw_fig.append([data_to_pre[index][-1], freq_lis[n][0], 0])
    data_to_draw_fig = np.array(data_to_draw_fig)

    # 抖动滤除
    if data_to_draw_fig.size == 0:
        # 处理空数据的情况
        pass
    else:
        # 计算每个时间点所属的时间窗口（左闭右闭区间）
        times = data_to_draw_fig[:, 0] * 10
        t_windows = np.where(times % 1 == 0, times - 1, np.floor(times)).astype(int)

        # 组合时间窗口和频率作为复合键
        freqs = data_to_draw_fig[:, 1]
        compound_keys = np.column_stack((t_windows, freqs))

        # 计算唯一键及其索引
        unique_keys, inverse_indices = np.unique(compound_keys, axis=0, return_inverse=True)

        # 计算每个组的平均强度
        sums = np.bincount(inverse_indices, weights=data_to_draw_fig[:, 2])
        counts = np.bincount(inverse_indices)
        averages = np.where(counts > 0, sums / counts, 0)

        # 更新原数组的强度值
        data_to_draw_fig[:, 2] = averages[inverse_indices]

    # 记录结束时间
    end_time = time.time()

    # 计算处理时间
    processing_time = end_time - start_time
    print(f"Processing time: {processing_time:.4f} seconds")

    write_to_py_file(output_py_name='res_data.py',output_data=data_print(cwt_res), write_mode='num')
    write_to_py_file(output_py_name='res_data_2.py',output_data=pre_data[0:87], write_mode='num')

    draw_time_freq_map(cwt_res[:,0], cwt_res[:,1], cwt_res[:,2], x_lim=[0, 20], y_lim=[20, 5000], ctrl_contrast=0.5, ctrl_hide = 0.0)
    draw_time_freq_map(data_to_draw_fig[:,0], data_to_draw_fig[:,1], data_to_draw_fig[:,2], x_lim=[0, 20], y_lim=[20, 5000], ctrl_contrast=1.0, ctrl_hide = 0.0)


